/*
* This file is a part of the open-eBackup project.
* This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
* If a copy of the MPL was not distributed with this file, You can obtain one at
* http://mozilla.org/MPL/2.0/.
*
* Copyright (c) [2024] Huawei Technologies Co.,Ltd.
*
* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
* EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
* MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
*/
#include "ObjectStorageBackupJob.h"
#include "JsonFileTool.h"
#include "obs_common/NodeTaskManager.h"

using namespace Module;
using namespace OBSPlugin;
namespace {
    const std::string MODULE = "ObjectStorageBackupMultiNode";
    constexpr int REPORT_MONITOR_TIME_1S = 1;
    constexpr uint32_t NUMBER100 = 100;
    constexpr uint32_t NUMBER10 = 10;
    const std::string TRUE_STR = "true";
    const std::string FALSE_STR = "false";
}

bool ObjectStorageBackupJob::CreateSubJobForMutiNodeBackup()
{
    DBGLOG("Enter CreateSubJobForMutiNodeBackup, jobId: %s", m_jobId.c_str());
    std::string label = "多节点并行备份开关打开,存储节点数:" + std::to_string(m_dataFsPathMap.size());
    // object_storage_plugin_multi_node_backup_with_one_stroage_warn_label
    JobLogLevel::type logLevel = m_dataFsPathMap.size() > 1 ?
        JobLogLevel::TASK_LOG_INFO : JobLogLevel::TASK_LOG_WARNING;
   
    ReportJobDetailsWithLabel(SubJobStatus::RUNNING,
        OBSPlugin::PROGRESS0,
        label,
        logLevel,
        std::to_string(m_dataFsPathMap.size()));
    if (!InitCloudClient()) {
        ERRLOG("InitCloudClient failed, jobId: %s", m_jobId.c_str());
        return false;
    }
    // 获取上一个副本对应的桶前缀信息
    std::vector<BackupObjectInfo> prevBackupObjectInfos;
    if (!BuildLastCopyExtendInfo(prevBackupObjectInfos)) {
        ERRLOG("BuildLastCopyExtendInfo failed, jobId: %s", m_jobId.c_str());
        return false;
    }
    // 按照层级异步扫描桶前缀信息
    auto ret = std::async(std::launch::async,
        std::bind(&ObjectStorageBackupJob::StartListPrefix, this));
    while (!m_isListPrefixFinish) {
        SendJobReportForAliveness();
        Module::SleepFor(std::chrono::seconds(REPORT_MONITOR_TIME_1S));
    }
    m_isListPrefixFinish = false;
    if (!ret.get()) {
        ERRLOG("ListPrefix failed");
        return false;
    }
    std::vector<std::string> repoEsnList;
    for (const auto& kvPair : m_dataFsPathMap) {
        repoEsnList.push_back(kvPair.first);
    }
    NodeTaskManager nodeTaskManager;
    std::vector<BackupObjectInfo> newBackupObjectInfos = nodeTaskManager.AllocateNodePrefix(m_bucketPrefixList,
        repoEsnList, prevBackupObjectInfos);
    if (CreateSubJobFromBackupObjectInfo(newBackupObjectInfos) != Module::SUCCESS) {
        ERRLOG("CreateSubJobFromBackupObjectInfo failed, jobId: %s", m_jobId.c_str());
        return false;
    }
    BackupObjectInfosInCopy backupObjectInfosInCopy;
    backupObjectInfosInCopy.advParms = m_protectedOBS.advParms;
    if (IsFullBackup()) {
        backupObjectInfosInCopy.objectInfos.swap(newBackupObjectInfos);
    } else {
        backupObjectInfosInCopy.objectInfos.swap(prevBackupObjectInfos);
    }
    return WriteBackupObjectInfoToFile(backupObjectInfosInCopy);
}

bool ObjectStorageBackupJob::StartListPrefix()
{
    m_bucketPrefixList.clear();
    if (!ListBucketPrefixs(m_bucketPrefixList)) {
        m_isListPrefixFinish = true;
        ERRLOG("ListBucketPrefixs failed, jobId: %s", m_jobId.c_str());
        return false;
    }
    m_isListPrefixFinish = true;
    return true;
}

bool ObjectStorageBackupJob::BuildLastCopyExtendInfo(std::vector<BackupObjectInfo> &backupObjectInfo)
{
    if (IsFullBackup()) {
        INFOLOG("Full backup, do not need get last copy, jobId: %s", m_jobId.c_str());
        return true;
    }
    Copy lastCopy;
    bool queryRet = QueryPreviousCopy(lastCopy);
    if (!queryRet) {
        return false;
    }
    BackupObjectInfosInCopy backupObjectInfosInCopy;
    if (!Module::JsonHelper::JsonStringToStruct(lastCopy.extendInfo, backupObjectInfosInCopy)) {
        ERRLOG("parse last copy extend info failed. copy extendInfo is: %s,jobId:%s",
            lastCopy.extendInfo.c_str(), m_jobId.c_str());
        return false;
    }
    backupObjectInfo.swap(backupObjectInfosInCopy.objectInfos);
    DBGLOG("query last copy extend info success. copy extendInfo is: %s,jobId:%s",
        lastCopy.extendInfo.c_str(), m_jobId.c_str());
    return true;
}

bool ObjectStorageBackupJob::ListBucketPrefixs(std::vector<PrefixInfo>& bucketPrefix)
{
    for (const auto& protectObject : m_protectedOBS.obsProtectSubObjectList) {
        std::vector<std::string> prefixs;
        if (protectObject.prefixs.empty()) {
            prefixs.push_back("");
        } else {
            prefixs.assign(protectObject.prefixs.begin(), protectObject.prefixs.end());
        }
        for (auto prefix : prefixs) {
            PrefixInfo prefixInfo;
            if (!ListBucketObjectsByDepth(protectObject.bucketName, prefix, prefixInfo)) {
                return false;
            }
            PrefixInfo tempPrefixInfo = prefixInfo;
            if (tempPrefixInfo.isPrefixLevelObject) { // 代表该perfix层级上存在对象
                tempPrefixInfo.subPrefixs.clear();
                bucketPrefix.push_back(tempPrefixInfo);
            }
            if (!prefixInfo.subPrefixs.empty()) {
                prefixInfo.isPrefixLevelObject = false;
                bucketPrefix.push_back(prefixInfo);
            }
        }
    }
    for (auto prefix : bucketPrefix) {
        DBGLOG("List pefix: %s", prefix.buildKey().c_str());
    }
    return true;
}

bool ObjectStorageBackupJob::ListBucketObjectsByDepth(const std::string& bucketName,
    std::string& prefix, PrefixInfo& prefixInfo)
{
    int maxDepth = std::stoi(m_protectedOBS.advParms.m_prefixSplitDepth);
    int curDepth = 0;
    std::deque<std::string> levelPrefixQueue;
    levelPrefixQueue.push_back(prefix);
    while (!levelPrefixQueue.empty() && curDepth < maxDepth) {
        curDepth++;
        if (!LevelListObject(bucketName, curDepth, levelPrefixQueue, prefixInfo)) {
            return false;
        }
        if (IsAbortJob()) {
            WARNLOG("Exit received Abort for taskid:%s, subTaskId:%s", m_jobId.c_str(), m_subJobId.c_str());
            return true;
        }
    }
    if (curDepth != maxDepth) {
        prefixInfo.isPrefixLevelObject = true;
    }
    prefixInfo.bucketName = bucketName;
    prefixInfo.parentPrefixFilter.push_back(prefix);
    DBGLOG("ListBucketObjectsByDepth finished,bucket:%s,parentPrefix:%s, subPrefixSize:%d",
        bucketName.c_str(), prefix.c_str(), prefixInfo.subPrefixs.size());
    return true;
}

bool ObjectStorageBackupJob::LevelListObject(const std::string& bucketName,
    int level, std::deque<std::string>& levelPrefixQueue, PrefixInfo& prefixInfo)
{
    int levelSize = levelPrefixQueue.size();
    int maxDepth = std::stoi(m_protectedOBS.advParms.m_prefixSplitDepth);
    std::string prefixDelimiter = m_protectedOBS.advParms.m_prefixSplitter;
    std::string prefixFilter;
    std::string marker = "";
    bool isTruncated = false;
    for (int i = 0; i < levelSize; i++) { // 开始第一层遍历
        prefixFilter = levelPrefixQueue.front();
        levelPrefixQueue.pop_front();
        DBGLOG("LevelListObject,current level:%d,prefixPos:%d, prefixSize:%d, list prefix: %s",
            level, i, levelSize, prefixFilter.c_str());
        do {
            if (IsAbortJob()) {
                WARNLOG("Exit received Abort for taskid:%s, subTaskId:%s", m_jobId.c_str(), m_subJobId.c_str());
                return true;
            }
            std::unique_ptr<ListObjectsRequest> req = std::make_unique<ListObjectsRequest>();
            std::unique_ptr<ListObjectsResponse> resp;
            if (req == nullptr) {
                ERRLOG("Make list objects request failed.");
                return false;
            }
            req->bucketName = bucketName;
            req->prefix = prefixFilter;
            req->delimiter = prefixDelimiter;
            req->marker = marker;
            OBSResult ret = m_cloudClient->ListObjects(req, resp);
            if (!ret.IsSucc()) {
                ERRLOG("Failed to ListObjects bucket: %s, prefix: %s, curDepth:%d",
                    bucketName.c_str(), prefixFilter.c_str(), level);
                return false;
            }
            if (!prefixInfo.isPrefixLevelObject && resp->contents.size() > 0) {
                prefixInfo.isPrefixLevelObject = true;
            }
            levelPrefixQueue.insert(levelPrefixQueue.end(), resp->commonPrefixes.begin(),
                resp->commonPrefixes.end());
            if (level == maxDepth) {
                prefixInfo.subPrefixs.insert(prefixInfo.subPrefixs.end(),
                    resp->commonPrefixes.begin(), resp->commonPrefixes.end());
            }
            marker = resp->nextMarker;
            isTruncated = resp->isTruncated;
        } while (isTruncated);
    }
    return true;
}
bool ObjectStorageBackupJob::IsContinueOnNodeFailed()
{
    return m_protectedOBS.advParms.m_continueOnFailedSwitch == TRUE_STR;
}

void ObjectStorageBackupJob::SortProtectSubObjectList(std::vector<OBSProtectSubObject>& protectSubObjectList)
{
    std::sort(protectSubObjectList.begin(), protectSubObjectList.end(),
        [](OBSProtectSubObject &a, OBSProtectSubObject &b)->bool {
            return a.bucketName < b.bucketName;
        });
}

bool ObjectStorageBackupJob::IsProtectedObjectsChanged()
{
    auto& preSubObjectList = m_prevBackupRecord.m_obsProtectSubObjectList;
    auto& curSubObjectList = m_protectedOBS.obsProtectSubObjectList;
    int preObjectSize = preSubObjectList.size();
    int curObjectSize = curSubObjectList.size();
    if (preObjectSize != curObjectSize) {
        return true;
    }
    SortProtectSubObjectList(preSubObjectList);
    SortProtectSubObjectList(curSubObjectList);
    for (int i = 0; i < curObjectSize; i++) {
        if (preSubObjectList[i].bucketName != curSubObjectList[i].bucketName) {
            return true;
        }
        if (IsPrefixsChanged(curSubObjectList[i], preSubObjectList[i])) {
            return true;
        }
    }
    return false;
}

bool ObjectStorageBackupJob::IsPrefixsChanged(OBSProtectSubObject& curObject, OBSProtectSubObject& preObject)
{
    if (curObject.prefixs.empty() && preObject.prefixs.empty()) {
        return false;
    } else if (curObject.prefixs.empty() || preObject.prefixs.empty()) {
        return true;
    }
    if ((curObject.prefixs.size() == preObject.prefixs.size())
        && std::is_permutation(curObject.prefixs.begin(), curObject.prefixs.end(), preObject.prefixs.begin())) {
        return false;
    }
    DBGLOG("Protected object prefixs changed, bucketName: %s", curObject.bucketName.c_str());
    return true;
}

bool ObjectStorageBackupJob::IsAclTypeChanged()
{
    return m_protectedOBS.advParms.m_isBackupAcl != m_prevBackupRecord.m_isBackupAcl;
}

bool ObjectStorageBackupJob::IsMultiNodeBackupSwitchChanged()
{
    return m_protectedOBS.advParms.m_multiNodeBackupSwitch !=
        m_prevBackupRecord.m_multiNodeBackupSwitch;
}

std::string ObjectStorageBackupJob::GetStorageStatusExtend(int64_t errCode)
{
    if (!IsMultiNodeBackup()) {
        return "";
    }
    static const std::vector<int64_t> storageAbnormalErrors {
        ObsErrorCode::ERROR_BACKUP_FAILED_NOSPACE_ERROR,
        ObsErrorCode::ERROR_OBS_BACKUP_SECONDARY_SERVER_NOT_REACHABLE
    };
    std::string storageAbnormal = FALSE_STR;
    for (const auto err : storageAbnormalErrors) {
        if (err == errCode) {
            storageAbnormal = TRUE_STR;
            break;
        }
    }
    if (storageAbnormal == TRUE_STR) {
        // 清理备份已拷贝的部分数据统计
        std::string backupStatPath = m_cacheFsPath + "/obs-statistics/backup-stats-sub-" + m_subJobId + ".json";
        OBSPlugin::RemoveFile(backupStatPath);
    }
    StorageStatusInfo storageStatus;
    storageStatus.storageAbnormal = storageAbnormal;
    std::string subJobExtendStr;
    if (!Module::JsonHelper::StructToJsonString(storageStatus, subJobExtendStr)) {
        ERRLOG("Convert StorageStatus to json failed");
    }
    return subJobExtendStr;
}

int ObjectStorageBackupJob::SwitchStorageForMutiNodeBackup(const ObjectStorageBackupSubJob& backupSubJob)
{
    std::string abnormalEsn = backupSubJob.esn;
    if (!IsContinueOnNodeFailed()) {
        std::string label = "存储节点:" + abnormalEsn + "故障,节点故障继续备份开关关闭";
        // object_storage_plugin_multi_node_backup_with_stroage_error_label
        ReportJobDetailsWithLabel(SubJobStatus::FAILED, OBSPlugin::PROGRESS0, label,
            JobLogLevel::TASK_LOG_ERROR, std::to_string(m_dataFsPathMap.size()));
        return Module::FAILED;
    }
    INFOLOG("Enter switch storage for mulitnode backup,abnormalEsn:%s", abnormalEsn.c_str());
    if (!LockStorageGeneralResources(m_jobId)) {
        ERRLOG("SwitchStorageForMutiNodeBackup,lock storage general resources failed");
        return Module::FAILED;
    }

    StorageGeneralInfo storageGeneralInfo;
    if (!GetStorageGeneralResources(m_jobId, storageGeneralInfo)) {
        UnLockStorageGeneralResources(m_jobId);
        ERRLOG("GetStorageGeneralResources failed, abnormalEsn:%s", abnormalEsn.c_str());
        return Module::FAILED;
    }
    // 多个子任务执行切换，查看该ESN是否已经切换，同一个ESN只切换一次
    if (!CheckSubJobNeedSwitchStorage(abnormalEsn, storageGeneralInfo)) {
        UnLockStorageGeneralResources(m_jobId);
        ReportJobDetails(JobLogLevel::TASK_LOG_INFO, SubJobStatus::COMPLETED, OBSPlugin::PROGRESS100);
        return Module::SUCCESS;
    }
    
    // 检查并更新存储状态信息
    BackupObjectInfosInCopy backupObjectInfosInCopy;
    bool checkRet = CheckAndGetObjectInfo(abnormalEsn, storageGeneralInfo,
        backupObjectInfosInCopy);
    UnLockStorageGeneralResources(m_jobId);
    if (!checkRet) {
        ERRLOG("CheckAndGetObjectInfo failed,jobId=%s", m_jobId.c_str());
        return Module::FAILED;
    }

    auto subJob = backupSubJob;
    if (!ReallocateSubJob(subJob, backupObjectInfosInCopy)) {
        ERRLOG("ReallocateSubJob failed,jobId=%s", m_jobId.c_str());
        return Module::FAILED;
    }
    ReportSwitchLabel(subJob, abnormalEsn);
    if (!ClearStatusBeforeSwitch()) {
        return Module::FAILED;
    }
    return ExecutePrefixScanSubJob(subJob);
}

void ObjectStorageBackupJob::ReportSwitchLabel(ObjectStorageBackupSubJob& backupSubJob,
    const std::string& abnormalEsn)
{
    std::string subTaskInfoStr;
    if (!Module::JsonHelper::StructToJsonString(backupSubJob, subTaskInfoStr)) {
        HCP_Log(WARN, MODULE) << "Convert to json failed for prefixInfo info:" << HCPENDLOG;
    }
    HCP_Log(INFO, MODULE) << "Start Trans,Subtask:" << subTaskInfoStr << HCPENDLOG;
    // 发生切换上报告警Label
    std::string label = "存储节点:" + abnormalEsn + "故障,切换到节点:" + backupSubJob.esn;
    // object_storage_plugin_multi_node_backup_with_stroage_switch_warn_label
    ReportJobDetailsWithLabel(SubJobStatus::RUNNING,
        OBSPlugin::PROGRESS0, label, JobLogLevel::TASK_LOG_WARNING,
        std::to_string(m_dataFsPathMap.size()));
}

bool ObjectStorageBackupJob::ClearStatusBeforeSwitch()
{
    m_scanStats.ClearScannedPrefixInfo();
    if (!UpdateScanStatsResource(m_jobId, m_scanStats)) {
        HCP_Log(ERR, MODULE) << "Update Scan Stats failed for switch storage!" << HCPENDLOG;
        return false;
    }
    return true;
}

bool ObjectStorageBackupJob::ReallocateSubJob(ObjectStorageBackupSubJob& backupSubJob,
    BackupObjectInfosInCopy& backupObjectInfosInCopy)
{
    bool isExist = false;
    auto it = backupObjectInfosInCopy.objectInfos.begin();
    for (; it != backupObjectInfosInCopy.objectInfos.end();) {
        if ((*it).esn == backupSubJob.esn) {
            backupSubJob.prefixInfo.swap((*it).prefixInfo);
            it = backupObjectInfosInCopy.objectInfos.erase(it);
            isExist = true;
            break;
        }
        ++it;
    }
    if (!isExist || backupObjectInfosInCopy.objectInfos.empty()) {
        ERRLOG("ReallocateSubJob failed,object info is empty.");
        return false;
    }
    // 分配一个正常的ESN,选择第一个存储节点
    auto &selectedObjectInfo = backupObjectInfosInCopy.objectInfos[0];
    backupSubJob.storageAbnormal = FALSE_STR;
    backupSubJob.esn = selectedObjectInfo.esn;
    // 更新分配信息，对前缀进行合并并且写入文件用于任务上报
    auto &prefixInfo = selectedObjectInfo.prefixInfo;
    prefixInfo.insert(prefixInfo.end(),
        backupSubJob.prefixInfo.begin(), backupSubJob.prefixInfo.end());
    if (!WriteBackupObjectInfoToFile(backupObjectInfosInCopy)) {
        ERRLOG("SwitchStorageForMutiNodeBackup failed,write object info failed.");
        return false;
    }
    return true;
}

bool ObjectStorageBackupJob::CheckAndGetObjectInfo(const std::string& esn,
    StorageGeneralInfo& storageGeneralInfo,
    BackupObjectInfosInCopy& backupObjectInfosInCopy)
{
    if (!JsonFileTool::ReadFromFile(m_backupObjectInfoFilePath, backupObjectInfosInCopy)) {
        ERRLOG("Read backup object info from file failed,abnormalEsn:%s", esn.c_str());
        return false;
    }
    // 只剩下一个节点，不能执行切换
    if (backupObjectInfosInCopy.objectInfos.size() <= 1) {
        ERRLOG("SwitchStorageForMutiNodeBackup failed,only one storage node");
        return false;
    }

    StorageStatusInfo storageStatusInfo;
    storageStatusInfo.esn = esn;
    storageStatusInfo.storageAbnormal = TRUE_STR;
    storageGeneralInfo.storageStatus.push_back(storageStatusInfo);
    if (!UpdateStorageGeneralResources(m_jobId, storageGeneralInfo)) {
        ERRLOG("SwitchStorageForMutiNodeBackup failed,UpdateStorageGeneralResources failed.");
        return false;
    }
    return true;
}

bool ObjectStorageBackupJob::CheckSubJobNeedSwitchStorage(const std::string& esn,
    const StorageGeneralInfo& storageGeneralInfo)
{
    // 多个子任务上报同一个存储设备异常时，判断是否已发生过切换
    for (auto storageStatus : storageGeneralInfo.storageStatus) {
        if (storageStatus.esn == esn) {
            WARNLOG("Storage has switched, abnormalEsn:%s", esn.c_str());
            return false;
        }
    }
    // 上次记录的统计信息存在异常ESN，代表已经发生过一次切换，不循环切换。
    if (storageGeneralInfo.storageStatus.size() > 0) {
        WARNLOG("Storage has switched, abnormalEsn:%s", esn.c_str());
        return false;
    }
    return true;
}

bool ObjectStorageBackupJob::LockStorageGeneralResources(const std::string& jobId)
{
    std::string key =  jobId + OBSPlugin::OBS_GLOBAL_STORAGE_STATUS_KEY;
    return LockResource(key, NUMBER100, jobId);
}

bool ObjectStorageBackupJob::UnLockStorageGeneralResources(const std::string& jobId)
{
    std::string key =  jobId + OBSPlugin::OBS_GLOBAL_STORAGE_STATUS_KEY;
    return UnlockResource(key, NUMBER10, jobId);
}

bool ObjectStorageBackupJob::GetStorageGeneralResources(const std::string& jobId, StorageGeneralInfo& generalInfo)
{
    std::string key =  jobId + OBSPlugin::OBS_GLOBAL_STORAGE_STATUS_KEY;
    AppProtect::ResourceStatus res {};
    if (!QueryResource(key, res, m_jobId, m_subJobId)) {
        ERRLOG("Get resource failed for key:%s", key.c_str());
        return false;
    }
    if (!Module::JsonHelper::JsonStringToStruct(res.resource.resourceValue, generalInfo)) {
        ERRLOG("Get resource failed (convert to JSON failed) for key:%s", key.c_str());
        return false;
    }
    DBGLOG("GetStorageGeneralResources key:%s,val:%s", key.c_str(), res.resource.resourceValue.c_str());
    return true;
}

bool ObjectStorageBackupJob::CreateStorageGeneralResources(const std::string& jobId, StorageGeneralInfo& generalInfo)
{
    if (GetStorageGeneralResources(jobId, generalInfo)) {
        WARNLOG("Get storage general resource has already exist,jobId:%s", jobId.c_str());
        return true;
    }
    std::string key =  jobId + OBSPlugin::OBS_GLOBAL_STORAGE_STATUS_KEY;
    std::string generalInfoString;
    if (!Module::JsonHelper::StructToJsonString(generalInfo, generalInfoString)) {
        ERRLOG("CreateStorageGeneralResources (convert to JSON failed) for key:%s", key.c_str());
        return false;
    }
    WARNLOG("Get storage general resource string,str:%s", generalInfoString.c_str());
    if (!CreateResource(key, generalInfoString, m_jobId, m_subJobId)) {
        ERRLOG("CreateStorageGeneralResources failed, key:%s", key.c_str());
        return false;
    }
    DBGLOG("CreateStorageGeneralResources key:%s,val:%s", key.c_str(), generalInfoString.c_str());
    return true;
}

bool ObjectStorageBackupJob::UpdateStorageGeneralResources(const std::string& jobId, StorageGeneralInfo& generalInfo)
{
    std::string key = jobId + OBSPlugin::OBS_GLOBAL_STORAGE_STATUS_KEY;
    DBGLOG("UpdateStorageGeneralResources for key:%s", key.c_str());
    Resource res {};
    res.__set_sharedNum(1);
    res.__set_scope(ResourceScope::SINGLE_JOB);
    res.__set_scopeKey(key);
    res.__set_resourceKey(key);

    std::string generalInfoString;
    if (!Module::JsonHelper::StructToJsonString(generalInfo, generalInfoString)) {
        ERRLOG("Update resource failed (convert to JSON failed) for key:%s", key.c_str());
        return false;
    }
    res.__set_resourceValue(generalInfoString);
    if (!UpdateResource(key, res, m_jobId, m_subJobId)) {
        ERRLOG("UpdateStorageGeneralResources failed for key:%s", key.c_str());
        return false;
    }
    return true;
}

bool ObjectStorageBackupJob::DeleteStorageGeneralResources(const std::string& jobId)
{
    if (!IsMultiNodeBackup()) {
        return true;
    }
    std::string key = jobId + OBSPlugin::OBS_GLOBAL_STORAGE_STATUS_KEY;
    DBGLOG("DeleteStorageGeneralResources key is:%s", key.c_str());
    if (!DeleteResource(key, m_jobId, m_subJobId)) {
        ERRLOG("DeleteStorageGeneralResources resource failed for key:%s", key.c_str());
        return false;
    }
    return true;
}

bool ObjectStorageBackupJob::SetRepoAbnormalFlag(const std::vector<BackupObjectInfo>& objectInfos,
    std::vector<StorageRepository>& repositories)
{
    for (auto& repo : repositories) {
        Json::Value extendInfoJsonVal;
        if (!Module::JsonHelper::JsonStringToJsonValue(repo.extendInfo, extendInfoJsonVal)) {
            ERRLOG("Convert to extendInfoJsonVal failed");
            return false;
        }
        if (!(extendInfoJsonVal.isObject()
            && extendInfoJsonVal.isMember("esn")
            && extendInfoJsonVal["esn"].isString())) {
            ERRLOG("Repo extendInfoJsonVal no esn field");
            return false;
        }
        const std::string dataFsEsn = extendInfoJsonVal["esn"].asString();
        bool isExist = CheckIsExistEsn(dataFsEsn, objectInfos);
        extendInfoJsonVal["isAbnormal"] = isExist ? FALSE_STR : TRUE_STR;
        Json::FastWriter jsonWriter;
        std::string extendInfo = jsonWriter.write(extendInfoJsonVal);
        repo.__set_extendInfo(extendInfo);
    }
    return true;
}

bool ObjectStorageBackupJob::CheckIsExistEsn(const std::string& esn, const std::vector<BackupObjectInfo>& objectInfos)
{
    for (const auto& objectInfo : objectInfos) {
        if (esn == objectInfo.esn) {
            return true;
        }
    }
    return false;
}